package pl.edu.pw.elka.jtrzasko.proz.warcaby.model;

import java.util.Iterator;
import java.util.LinkedList;

import pl.edu.pw.elka.jtrzasko.proz.warcaby.figures.Figure;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.figures.FigureColor;
import pl.edu.pw.elka.jtrzasko.proz.warcaby.figures.King;

/**
 * Glowna klasa modelu, udostepniajaca kontrolerowi metody umozliwiajace
 * sprawdzenie i zmiane stanu gry.
 */
public class Model {
	/** Plansza na ktorej operuje model. */
	private Board board;

	/**
	 * Utworzenie nowego modelu wraz z plansza oraz zainicjowaniem pozycji
	 * startowej figur na planszy.
	 */
	public Model() {
		board = new Board();
		board.setupFigures(FigureColor.BLACK, 0, 2);
		board.setupFigures(FigureColor.WHITE, 5, 7);
	}

	/**
	 * Przeniesienie piona z pozycji na pozycje
	 * 
	 * @param p1
	 *            Pozycja poczatkowa
	 * @param p2
	 *            Pozycja koncowa
	 */
	public void makeMove(final Position p1, final Position p2) {
		board.swapFields(p1, p2);
		board.removeFigures(p1.getFieldsFromLine(p2));
	}

	/**
	 * Sprawdzenie czy gra sie skonczyla w skutek zwycicestwa jednego z graczy
	 * 
	 * @return Prawda, jesli gra jest zakonczona
	 */
	public boolean isGameFinished() {
		return board.isAnyFigure(FigureColor.WHITE)
				&& board.isAnyFigure(FigureColor.BLACK) ? false : true;

	}

	/**
	 * Sprawdz czy mozliwa jest promocja figury znajdujacej sie na danej pozycji
	 * 
	 * @param p
	 *            Pole na ktorym znajduje sie figura
	 * @return Prawda, jesli promocja jest mozliwa.
	 */
	public boolean isPromotionValid(Position p) {
		if (!board.isFieldEmpty(p))
			return board.getFigure(p).isPromotionValid(board);
		return false;
	}

	/**
	 * Wykonanie promocji
	 * 
	 * @param p
	 *            Pole na ktorym znajduje sie figura promowana
	 */
	public void makePromotion(Position p) {
		board.setFigureOnField(new King(p, board.getFigure(p).getColor()));
	}

	/**
	 * Sparwdza czy przesuniecie figury z miejsca na miejsce jest poprawnym
	 * ruchem
	 * 
	 * @param p1
	 *            Pozycja startowa
	 * @param p2
	 *            Pozyja docelowa
	 * @return Prawda, jesli ruch jest poprawny
	 */
	public boolean isMoveValid(Position p1, Position p2) {
		if (board.getFigure(p1).isMoveValid(p2, board))
			return true;
		return false;
	}

	/**
	 * Sparwdza czy bicie figury z miejsca na miejsce jest poprawnym biciem
	 * 
	 * @param p1
	 *            Pozycja startowa
	 * @param p2
	 *            Pozyja docelowa
	 * @return Prawda, jesli bicie jest poprawne
	 */
	public boolean isBeatingValid(Position p1, Position p2) {
		if (board.getFigure(p1).isBeatingValid(p2, board))
			return true;
		return false;
	}

	/**
	 * Pobiera zestaw informacji o figurch znajdujay sie na planszy
	 * 
	 * @return Zestaw informacji o figurch znajdujay sie na planszy
	 */
	public LinkedList<Figure> getFigures() {
		return board.getFigures();
	}

	/**
	 * Sprawdza czy na denj pozycji znajduje sie jakas figura
	 * 
	 * @param p
	 *            Pozycja do sprawdzenia
	 * @return Prawda, jesli pole jest puste
	 */
	public boolean isFieldEmpty(Position p) {
		if (board.isFieldEmpty(p))
			return true;
		return false;
	}

	/**
	 * Sprawdza czy na danej pozycji znajduje sie figura o okreslonym kolorze.
	 * 
	 * @param p
	 *            Pozycja do sprawdzenia
	 * @param color
	 *            Szuakny kolor figury
	 * @return Prawda, jesli pole zawiera figure o okreslonym kolorze
	 */
	public boolean isFigureColor(Position p, FigureColor color) {
		if (board.getFigure(p).getColor() == color)
			return true;
		return false;
	}

	/**
	 * Sprawdza czy figura na danym polu moze wykonac jakiekolwiek bicie
	 * 
	 * @param position
	 *            Pozycja do sprawdzenia
	 * @return Prawda, jesli dana figura ma mozliwosc bicia
	 */
	public boolean isBeatingValid(Position position) {
		for (int i = 0; i < Board.LINE_MAX; i++)
			for (int j = 0; j < Board.LINE_MAX; j++)
				if (board.getFigure(position).isBeatingValid(new Position(i, j),
						board))
					return true;
		return false;
	}

	/**
	 * Sprawdza czy figura na danym polu moze wykonac jakiekolwiek ruch
	 * 
	 * @param position
	 *            Pozycja do sprawdzenia
	 * @return Prawda, jesli dana figura ma mozliwosc wykonania ruchu
	 */
	public boolean isFigureCanMove(Position position) {
		for (int i = 0; i < Board.LINE_MAX; i++)
			for (int j = 0; j < Board.LINE_MAX; j++)
				if (board.getFigure(position).isMoveValid(new Position(i, j),
						board))
					return true;
		return false;
	}

	/**
	 * Sprawdza czy jakakolwiek figura na planszy o danych kolorze ma mozliwosc
	 * bicia
	 * 
	 * @param color
	 *            Kolor figur do sprawdzenia
	 * @return Prawda, jesli istanieje przyjajmniej jedno potencjalne bicie
	 */
	public boolean isBeatingPosible(FigureColor color) {
		for (int i = 0; i < 8; i++)
			for (int j = 0; j < 8; j++)
				if (!board.isFieldEmpty(new Position(i, j))
						&& board.getFigure(new Position(i, j)).getColor() == color
						&& isBeatingValid(new Position(i, j)))
					return true;
		return false;
	}

	/**
	 * Sprawdza czy jakakolwiek figura na planszy o danych kolorze ma mozliwosc
	 * wykonania ruchu
	 * 
	 * @param color
	 *            Kolor figur do sprawdzenia
	 * @return Prawda, jesli istanieje przyjajmniej jeden potencjalny ruch
	 */
	public boolean isMovePosible(FigureColor color) {
		for (int i = 0; i < Board.LINE_MAX; i++)
			for (int j = 0; j < Board.LINE_MAX; j++)
				if (!board.isFieldEmpty(new Position(i, j))
						&& board.getFigure(new Position(i, j)).getColor() == color
						&& isFigureCanMove(new Position(i, j)))
					return true;
		return false;
	}

	/**
	 * Sprawdza czy gra nei skonczyla sie remisem
	 * 
	 * @param activePlayerColor
	 *            kolor aktywnego gracza
	 * @return Prawda, jesli gra skoczyla sie remisem
	 */
	public boolean isGameDraw(FigureColor activePlayerColor) {
		if (isMovePosible(activePlayerColor) || isBeatingPosible(activePlayerColor))
			return false;
		return true;
	}

}
